package t1

object Matrizen_v2 {
	
	object Matrix{
	  def apply(i:Int) = {
	    val a = Array(3,4,3)
	    a.deep
        new Matrix( for(i <- 0 until i) yield {for(i <- 0 until i) yield 0} )
    }
    
    
    def apply[N](ar:Array[Array[N]])(implicit num:Numeric[N]) = {
      val iter = ar.iterator
	  new Matrix( for(i <- 0 until ar.length) yield {ar(i).toIndexedSeq} )
    }
    
    
    def apply[N](n:Int, f: (Int,Int) => N)(implicit num:Numeric[N]) = {
      val ma = Array.fill(n, n)(0.0)
      for{i1 <- 0 until n; i2 <- 0 until n}{          
          ma(i1)(i2) = num.toDouble( f(i1,i2) )
      }
      new Matrix(for(i <- 0 until ma.length) yield {ma(i).toIndexedSeq})
    }
  }//object
	  
	
	class Matrix[N]( val matrix:IndexedSeq[IndexedSeq[N]])(implicit num:Numeric[N]){
		
	  def apply(i1:Int,i2:Int) = {
	    if(i1 >= 0 && i1 < matrix.length && matrix(0) != null){
	      if(i2 >= 0 && i2 < matrix(0).length ) matrix(i1)(i2)
	      else num.zero
	    }else{
	      num.zero
	    }	    
	  }
		
	  def apply[N](list:N*)(implicit num:Numeric[N]) = {
	    val iter = list.iterator
	    val ma = for(i <- matrix) yield { (0 until matrix.length).foldLeft(IndexedSeq[N]()){(accu,item) => accu:+{if(iter.hasNext) iter.next else num.minus(list(0),list(0))}} }
	    new Matrix(ma)
      }  
	  
	  override def toString =  matrix.toString
	  def length = matrix.length
	  
	  def +(o:Matrix[N])(implicit num: Numeric[N]) = {
	    if(matrix.length == o.length) for(i <- 0 until matrix.length) yield {for(j <- 0 until matrix.length) yield num.plus(matrix(0)(0), o.matrix(0)(0)) }
	    else matrix
	  }
	  
	  def *(o:Matrix[N]) = {
	    for(i <- 0 until length) yield {for(j <- 0 until length) yield (0 until length).foldLeft(num.zero){
	      (accu,item) => num.plus( num.times(matrix(i)(item), o.matrix(item)(j)), accu)
	    }}
	  }
	          
	    

		
	}//class
	
	def main(s:Array[String]){
	  test
    }
	
	def test {
	  println(Matrix(3)(-5,2,3))
	  println(Matrix(3)(1.0,2,3))
	  println(Matrix(3)(BigInt(7),BigInt(7),BigInt(7),BigInt(669)))
	  
	  val mat_5 = Matrix(Array(Array(6.6,5.5,7.7),Array(6.6,5.5,7,7),Array(6.6,5.5,7,7)))
	  println(mat_5)
	  
	  val mat1= Matrix(5,(i,j) => if(i==j) 1 else 0) // 5x5 Einheits-Matrix
	  println(mat1)
	  println(mat1(77,77))
	  val sum = mat_5+mat_5
	  println(sum)
	  
	  val op1 = Matrix(2)(1,2,3,4)
	  val op3 = Matrix(2)(4,3,2,1)
	  val op2 = Matrix(2)(BigInt(7),BigInt(7),BigInt(7),BigInt(669))
	  println( (op1*op3) )
//	  println( (op1*op2) )
	}
	  

}